package bot;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * Klasa gracza komputerowego
 * 
 * @author Kornel Mirkowski, Maciej Krawczyk
 */
public class Bot extends Thread {

	/** strategia BOT'a */
	private Strategy stg;

	/** Gniazdko sieciowe */
	private Socket socket;
	/** Strumien wyjsciowy */
	private PrintWriter out;
	/** Strumien wejsciowy */
	private BufferedReader in;

	/** Karty BOT'a */
	private String cards;
	/**
	 * Dane BOT'a (id, rola w grze, zetony, stawka, liczba graczy, przydzial
	 * zetonow, maksymalna stawka)
	 */
	private int id, blind, chips, stake, blindStake, players, allowance,
			maxStake;
	/** Flagi sterujace przeplywem rozgrywki */
	private boolean created, dealt, eor, bet;

	/** Konstruktor BOT'a przypisujacy mu strategie gry */
	public Bot() {
		stg = new Begginer();
	}

	/**
	 * Metoda uruchamia gracza komputerowego
	 * 
	 * @param args
	 *            host i port
	 */
	public static void main(String[] args) {

		// String[] tab = {"localhost", "44"};
		// args = tab;

		int port;
		try {
			port = Integer.parseInt(args[1]);
		} catch (Exception e) {
			System.out.println("Nieudane parsowanie portu");
			return;
		}
		Bot bot = new Bot();
		bot.connecting(args[0], port);
	}

	/**
	 * Metoda laczy BOT'a z wybranym pokojem
	 * 
	 * @param host
	 *            host (ip)
	 * @param port
	 *            numer portu
	 */
	public void connecting(String host, int port) {

		try {
			socket = new Socket(host, port);
			out = new PrintWriter(socket.getOutputStream(), true);
			in = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
			System.out.println("Polaczono");
		} catch (IOException e) {
			System.out.println("Nieudane nawiazanie polaczenia");
			return;
		}

		start();
	}

	/** Dopoki BOT w grze metoda przechwytuje informacje z serwera */
	@Override
	public void run() {
		while (true) {
			String order;
			try {
				order = in.readLine();
				System.out.println(order);
				react(order);
			} catch (IOException e) {
				// System.out.println("Nie przechwycono danych");
				System.out.println("I lost the game!");
				System.exit(-1);
			}
		}
	}

	/**
	 * Metoda symulujaca reakcje zywego gracza na odebrana informacje
	 * 
	 * @param order
	 *            komenda ze strony serwera
	 */
	public void react(String order) {

		if (!created) {
			install(order);
			created = true;
		} else if (!dealt) {
			deal(order);
			dealt = true;
		} else if (!eor) {

			String myTurn = Integer.toString(id);
			String myExchangeTurn = "E" + myTurn;

			if (myTurn.equals(order)) {
				String response = stg.bid(cards, chips, stake, maxStake, bet);
				System.out.println("karty: " + cards + " zetony: " + chips
						+ " stawka: " + stake + " max stawka: " + maxStake
						+ " czy mozna bet: " + bet + " ruch: " + response);
				execute(response);
				send(response);
			} else if (myExchangeTurn.equals(order)) {
				String response = stg.exchange(cards);
				send(response);
				try {
					String newCards = in.readLine();
					setCards(newCards);
					System.out.println(newCards);
				} catch (Exception e) {
					System.out.println("Nieudane odebranie nowych kart");
				}
			} else {

				if (order.substring(0, 1).equals("E")) {

					try {
						order = in.readLine();
						bet = true;
						System.out.println(order);
					} catch (Exception e) {
						System.out
								.println("Nie odczytano wymiany kart przeciwnika");
					}

				} else if (order.substring(0, 1).equals("W")) {

					int winner, prize;

					try {
						winner = Integer.parseInt(order.substring(1, 2));
						prize = Integer.parseInt(order.substring(2));
					} catch (Exception e) {
						System.out
								.println("Nieprawidlowy komounikat - wygrana gry");
						return;
					}
					if (winner == id) {
						setChips(prize + chips);
					}

					if (getChips() == 0) {
						System.out.println("I lost the game!");
						System.exit(0);
					} else if (getChips() == allowance * players) {
						System.out.println("I won the game!");
						System.exit(0);
					}

					skipOpCards();
					dealt = false;
					maxStake = 0;
				} else if (order.substring(0, 1).equals("D")) {

					if (getChips() == 0) {
						System.out.println("I lost the game!");
						System.exit(0);
					} else if (getChips() == allowance * players) {
						System.out.println("I won the game!");
						System.exit(0);
					}

					skipOpCards();
					dealt = false;
					maxStake = 0;
				} else {

					try {
						order = in.readLine();
						System.out.println(order);
						opMove(order);
					} catch (Exception e) {
						System.out.println("Nieznany ruch przeciwnika");
					}

				}
			}
		} else {
			// System.out.println("Nie przechwycono danych");
			System.out.println("I lost the game!");
			System.exit(-1);
		}

	}

	/**
	 * Metoda instaluje poczatkowe ustawienia gry
	 * 
	 * @param settings
	 *            ustawienia gry
	 */
	public void install(String settings) {

		try {
			id = Integer.parseInt(settings.substring(0, 1));
		} catch (NumberFormatException e) {
			return;
		}
		try {
			players = Integer.parseInt(settings.substring(1, 2));
		} catch (NumberFormatException e) {
			return;
		}
		try {
			chips = Integer.parseInt(settings.substring(2));
			allowance = chips;
		} catch (NumberFormatException e) {
			return;
		}
	}

	/**
	 * Metoda pobiera karty z serwera i ustawia odpowiednie dane
	 * 
	 * @param order
	 *            komenda wyslana przez serwer
	 */
	public void deal(String order) {

		try {
			blind = Integer.parseInt(order.substring(0, 1));
		} catch (NumberFormatException e) {
			return;
		}
		try {
			cards = order.substring(1, 9);
		} catch (StringIndexOutOfBoundsException e) {
			return;
		}
		try {
			blindStake = Integer.parseInt(order.substring(9));
			maxStake = blindStake * 2;
		} catch (NumberFormatException e) {
			return;
		}

		setStake(0);
		checkRole();
	}

	/** Ustawienie zakladu jesli moja rola to BB, albo SB */
	public void checkRole() {

		if (blind == 1) {

			if (chips >= blindStake) {
				setStake(blindStake);
				setChips(chips - blindStake);
			} else {
				setStake(chips);
				setChips(0);
			}
		} else if (blind == 2) {

			if (chips >= blindStake) {
				setStake(blindStake * 2);
				setChips(chips - (blindStake * 2));
			} else {
				setStake(chips);
				setChips(0);
			}
		}
	}

	/**
	 * Metoda wykonuje ruch BOT'a zmieniajac jego dane
	 * 
	 * @param response
	 *            odpowiedz strategii na dana sytuacje
	 */
	public void execute(String response) {

		String stake;
		int bid = -100000;
		switch (response.substring(0, 1)) {
		case "C":
			break;

		case "K":
			call();
			break;

		case "B":
			stake = response.substring(1);
			try {
				bid = Integer.parseInt(stake);
			} catch (Exception e) {
				System.out.println("Nieudane wykonanie Bet");
			}
			bid(bid);
			bet = false;
			break;

		case "R":
			stake = response.substring(1);
			try {
				bid = Integer.parseInt(stake);
			} catch (Exception e) {
				System.out.println("Nieudane wykonanie Bet lub Raise");
			}
			bid(bid);
			break;

		case "F":
			fold();
			break;

		case "A":
			allIn();
			break;

		default:
			break;
		}
	}

	/** Ruch Call */
	public void call() {
		setChips(chips - (getMaxStake() - this.stake));
		setStake(getMaxStake());
	}

	/**
	 * Podbicie aktualnej stawki na stole
	 * 
	 * @param stake
	 *            wysokosc stawki
	 */
	public void bid(int stake) {
		int newStake = stake + getMaxStake();
		setChips(chips - (newStake - this.stake));
		setStake(newStake);
		maxStake = newStake;
	}

	/** Zagranie All-In */
	public void allIn() {

		setStake(stake + chips);
		setChips(0);
	}

	/** Zagranie Fold */
	public void fold() {
		setStake(0);
	}

	/**
	 * Ustawienie obecnej stawki
	 * 
	 * @param stake
	 *            wysokosc stawki
	 */
	public void setStake(int stake) {
		this.stake = stake;
	}

	/**
	 * Akcesor do pola stake
	 * 
	 * @return wysokosc stawki
	 */
	public int getStake() {
		return stake;
	}

	/**
	 * Akcesor do pola maksymalnej stawki
	 * 
	 * @return wysokosc maksymalnej stawki
	 */
	public int getMaxStake() {
		return maxStake;
	}

	/**
	 * Ustawienie zetonow
	 * 
	 * @param chips
	 *            liczba zetonow
	 */
	public void setChips(int chips) {
		this.chips = chips;
	}

	/**
	 * Akcesor do pola chips
	 * 
	 * @return liczba zetonow
	 */
	public int getChips() {
		return chips;
	}

	/**
	 * Ustawienie kart
	 * 
	 * @param cards
	 *            karty
	 */
	public void setCards(String cards) {
		this.cards = cards;
	}

	/**
	 * Metoda wysyla wiadomosc na serwer
	 * 
	 * @param msg
	 *            wiadomosc
	 */
	public void send(String msg) {
		for (int i = 1; i > 0; i++) {}
		for (int i = 1; i > 0; i++) {}
		out.println(msg);
	}

	/** Pominiecie informacji o kartach przeciwnikow */
	public void skipOpCards() {

		for (int i = 0; i < players; i++) {
			try {
				cards = in.readLine();
				System.out.println(cards);
			} catch (IOException e) {
				System.out.println("Nie przechwycono kart (po rozgrywce)");
			}
		}
		send("O");
	}

	/**
	 * Metoda reaguje na ruchy przeciwnikow
	 * 
	 * @param order
	 *            informacja o ruchu przeciwnika
	 */
	public void opMove(String order) {

		int increase;
		String stake;
		String move = order.substring(1, 2);
		if (move.equals("B") || move.equals("R")) {

			if (move.equals("B"))
				bet = false;

			stake = order.substring(2);
			try {
				increase = Integer.parseInt(stake);
				maxStake += increase;
			} catch (Exception e) {
				System.out
						.println("Nie mozna pobrac stawki podbicia przeciwnika");
			}
		}
	}

}
